﻿/*
 * Cratos Tools - Data Object
 * Cratos Data Object Helper class
 * 
 * Copyright (c) 2011-2012 Draco
 * 
 * This file is part of Cratos.
 * 
 * Cratos is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * Cratos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cratos.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *       .==.        .==.
 *      //'^\\      //^'\\
 *     // ^ ^\(\__/)/^ ^^\\
 *    //^ ^^ ^/6  6\ ^^ ^ \\
 *   //^ ^^ ^/( .. )\^ ^ ^ \\
 *  // ^^ ^/\| V""V|/\^ ^ ^ \\
 * //^^/\/ /  '~~'  \ \/\^ ^ \\
 *  ------------------------------------
 *  HERE BE DRAGONS
 *  
 *  Johan Dercksen - johan@draco.co.za
 */
using System;
using System.Data;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;

using Cratos.Tools.General;

namespace Cratos.Tools.DataObject
{
	/// <summary>
	/// Cratos Data Object Helper class
	/// </summary>
	public static class DOCratosHelper
	{
		#region Class Constant declarations
		#endregion

		#region Class Event declarations
		#endregion

		#region Class Variable declarations
		#endregion

		#region Class Constructors & Destructors
		#endregion

		#region Class Properties
		#endregion

		#region Class Methods
		/// <summary>
		/// Get the Property value
		/// </summary>
		/// <param name="sourceObject">Source Object</param>
		/// <param name="propertyName">Property Name</param>
		/// <returns>The Value of the Object</returns>
		public static object GetPropertyValue(object sourceObject, string propertyName)
		{
			// Declare the required variables
			object returnValue = null;
			PropertyInfo sourceProperty;

			// Make sure we have a valid Source Object
			if (sourceObject != null)
			{
				// Retrieve the Source Property Details
				sourceProperty = sourceObject.GetType().GetProperty(propertyName);

				// Retrieve the object data
				if (sourceProperty != null)
				{
					returnValue = sourceProperty.GetValue(sourceObject, null);
				}
			}

			// Return to calling method
			return (returnValue);
		}

		/// <summary>
		/// Get the Property value
		/// </summary>
		/// <param name="sourceObject">Source Object</param>
		/// <param name="propertyName">Property Name</param>
		/// <param name="newValue">New Value to be set</param>
		public static void SetPropertyValue(object sourceObject, string propertyName, object newValue)
		{
			// Declare the required variables
			PropertyInfo sourceProperty;

			// Make sure we have a valid Source Object
			if (sourceObject != null)
			{
				// Retrieve the Source Property Details
				sourceProperty = sourceObject.GetType().GetProperty(propertyName);

				// Retrieve the object data
				if (sourceProperty != null)
				{
					Type sourceType = newValue.GetType();
					Type destinationType = sourceProperty.PropertyType;
					if (sourceType != destinationType && !destinationType.IsInstanceOfType(newValue))
					{
						if (newValue is Enum)
						{
							Type underlyingType = Enum.GetUnderlyingType(sourceType);
							if (destinationType.IsAssignableFrom(underlyingType))
							{
								SetPropertyValue(sourceObject, propertyName, Convert.ChangeType(newValue, underlyingType));
								return;
							}
						}

						bool couldConvert = false;
						Exception innerException = null;
						System.ComponentModel.TypeConverter converter = System.ComponentModel.TypeDescriptor.GetConverter(destinationType);

						if (converter != null)
						{
							try 
							{ 
								newValue = converter.ConvertFromString(newValue.ToString());
								couldConvert = true;
							}
							catch (NotSupportedException ex) 
							{ 
								innerException = ex; 
							}
						}
						
						if (!couldConvert)
						{
							throw new Exception(string.Format("{0}: cannot convert from {1} to {2}", propertyName, sourceType, destinationType), innerException);
						}
					}
	
					sourceProperty.SetValue(sourceObject, newValue, null);
				}
			}

			// Return to calling method
			return;
		}

		/// <summary>
		/// Invoke the relevant Method
		/// </summary>
		/// <param name="sourceObject">Source Object</param>
		/// <param name="methodName">Method Name</param>
		/// <param name="methodParameters">Method Parameters</param>
		/// <returns>The Return Value of the Method invoked</returns>
		public static object InvokeMethod(object sourceObject, string methodName, object[] methodParameters)
		{
			// Declare the required variables
			object			returnValue = null;
			MethodInfo	sourceMethod;

			// Make sure we have a valid Source Object
			if (sourceObject != null)
			{
				// Retrieve the Source Property Details
				sourceMethod = sourceObject.GetType().GetMethod(methodName);

				// Retrieve the object data
				if (sourceMethod != null)
				{
					returnValue = sourceMethod.Invoke(sourceObject, methodParameters);
				}
			}

			// Return to calling method
			return (returnValue);
		}

		/// <summary>
		/// Subscribe to an Event
		/// </summary>
		/// <param name="sourceObject">Source Object</param>
		/// <param name="eventName">Event Name</param>
		/// <param name="eventHandlerMethod">Event Handler Method</param>
		public static void SubscribeToEvent(object sourceObject, string eventName, Delegate eventHandlerMethod)
		{
			// Declare the required variables
			EventInfo	eventInfo;

			// Make sure we have a source object to work with
			if (sourceObject != null)
			{
				// Retrieve the Event Info
				eventInfo = sourceObject.GetType().GetEvent(eventName);

				// Make sure we have the relevant info
				if (eventInfo != null)
				{
					// Subscribe to the relevant event
					eventInfo.AddEventHandler(sourceObject, eventHandlerMethod);
				}
			}

			// Return to calling method
			return;
		}

		/// <summary>
		/// Contains Valid Value 
		/// </summary>
		/// <param name="firstValue">First Value</param>
		/// <param name="secondValue">Second Value</param>
		/// <param name="currentType">Data Type</param>
		/// <param name="isMandatory">Mandatory indicator</param>
		/// <param name="isIgnoreCompareIfNull">Ignore Compare if Second Value is Null</param>
		/// <returns></returns>
		public static bool CompareValues(object firstValue, object secondValue, DbType currentType, bool isMandatory, bool isIgnoreCompareIfNull)
		{
			// Declare the required variables
			bool returnValue = false;

			// Handle the relevant Type
			if ((firstValue == null) && (secondValue == null)) { returnValue = true; }
			else if (firstValue != null)
			{
				switch (currentType)
				{
					case DbType.Byte:
						// Validate the Mandatory indicator
						if (isMandatory && (Convert.ToInt16(firstValue) == 0)) { returnValue = false; }
						else if (Convert.ToInt16(firstValue) == 0) { return (true); }
						else if ((Convert.ToInt16(secondValue) == 0) && isIgnoreCompareIfNull) { return (true); }
						else
						{
							returnValue = Convert.ToInt16(firstValue) == Convert.ToInt16(secondValue);
						}

						break;

					case DbType.Int16:
					case DbType.Int32:
					case DbType.Int64:
						// Validate the Mandatory indicator
						if (isMandatory && (Convert.ToInt64(firstValue) == 0)) { returnValue = false; }
						else if (Convert.ToInt64(firstValue) == 0) { return (true); }
						else if ((Convert.ToInt64(secondValue) == 0) && isIgnoreCompareIfNull) { return (true); }
						else
						{
							returnValue = Convert.ToInt64(firstValue) == Convert.ToInt64(secondValue);
						}

						break;

					case DbType.DateTime:
						// Validate the Mandatory indicator
						if (isMandatory && (Convert.ToDateTime(firstValue).Year <= 1753)) { returnValue = false; }
						else if (Convert.ToDateTime(firstValue).Year <= 1753) { return (true); }
						else if ((Convert.ToDateTime(secondValue).Year <= 1753) && isIgnoreCompareIfNull) { return (true); }
						else
						{
							returnValue = Convert.ToDateTime(firstValue).Equals(Convert.ToDateTime(secondValue));
						}

						break;

					case DbType.Decimal:
						// Validate the Mandatory indicator
						if (isMandatory && (Convert.ToDecimal(firstValue) == Decimal.Zero)) { returnValue = false; }
						else if (Convert.ToDecimal(firstValue) == Decimal.Zero) { return (true); }
						else if ((Convert.ToDecimal(secondValue) == Decimal.Zero) && isIgnoreCompareIfNull) { return (true); }
						else
						{
							returnValue = Convert.ToDecimal(firstValue).Equals(Convert.ToDecimal(secondValue));
						}

						break;

					case DbType.String:
						// Validate the Mandatory indicator
						if (isMandatory && string.IsNullOrEmpty((string)firstValue)) { returnValue = false; }
						else if (string.IsNullOrEmpty((string)firstValue)) { return (true); }
						else if (string.IsNullOrEmpty((string)secondValue) && isIgnoreCompareIfNull) { return (true); }
						else
						{
							returnValue = firstValue.ToString().Equals(secondValue.ToString());
						}

						break;

					case DbType.Guid:
						// Validate the Mandatory indicator
						if (isMandatory && ((Guid)firstValue == Guid.Empty)) { returnValue = false; }
						else if ((Guid)firstValue == Guid.Empty) { return (true); }
						else if (((Guid)secondValue == Guid.Empty) && isIgnoreCompareIfNull) { return (true); }
						else
						{
							returnValue = ((Guid)firstValue).Equals((Guid)secondValue);
						}

						break;

					case DbType.Boolean:
						// Validate the Mandatory indicator
						returnValue = true;

						break;

					default:
						// Validate the Mandatory indicator
						if (isMandatory && (firstValue == null)) { returnValue = false; }
						else if (firstValue == null) { return (true); }
						else if ((secondValue == null) && isIgnoreCompareIfNull) { return (true); }
						else
						{
							returnValue = firstValue.Equals(secondValue);
						}

						break;
				}
			}

			// Return to calling method
			return (returnValue);
		}

		/// <summary>
		/// Is Type Nullable 
		/// </summary>
		/// <param name="typeToTest"></param>
		/// <returns>A boolean indicating whether the type is nullable</returns>
		public static bool IsTypeNullable(Type typeToTest)
		{
			// Declare the required variables

			// Abort if no type supplied 
			if (typeToTest == null) { return(false); }

			// If this is not a value type, it is a reference type, so it is automatically nullable 
			if (!typeToTest.IsValueType) { return (true); }

			// Report whether TypeToTest is a form of the Nullable<> type 
			return (typeToTest.IsGenericType && (typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>)));
		}
		
		/// <summary>
		/// Retrieve the Nullable underlying type
		/// </summary>
		/// <param name="typeToConvert"></param>
		/// <returns></returns>
		public static Type GetNullableType(Type typeToConvert) 
		{
			// Declare the required variables

			// Abort if no type supplied 
			if (typeToConvert == null) { return (null); }
 
			// If the given type is already nullable, just return it 
			if (DOCratosHelper.IsTypeNullable(typeToConvert)) { return(typeToConvert); }
 
			// If the type is a ValueType and is not System.Void, convert it to a Nullable<Type> 
			if (typeToConvert.IsValueType && typeToConvert != typeof(void))
			{
				return(typeof(Nullable<>).MakeGenericType(typeToConvert));
			}
 
			// Done - no conversion 
			return(null); 
		}
		#endregion

		#region Class Support Methods
		#endregion
	}
}
